/*
*		
*		A compilacao e feita por: gcc -o montecarlo_pthread montecarlo_pthread.c -lgmp -lm -lpthread
*
*/

#include <stdio.h>
#include <unistd.h>
#include <limits.h>
#include <stdlib.h>
#include <fcntl.h>
#include <time.h>
#include <math.h>
#include <gmp.h> 
#include <pthread.h>

#define N_THREADS 2

#define KICKS  ULONG_MAX/(N_THREADS*5)
#define BASE 10
#define MANTISSA 1024*1024*32  

unsigned long acc[N_THREADS];

// cria semaforo
pthread_mutex_t MUTEX;


//gera valores aleatorios
int get_random()
{
	int fd, nrandom;
	fd = open("/dev/random", O_RDONLY);
	read(fd, &nrandom, sizeof(nrandom));
	close(fd);
	return nrandom;
}


// roda KICKS vezes para cada thread, recebe o id da thread
// escolhe numeros aleatorios para X e Y
// R = sqrt(X^2) + (Y^2)
// se R for menor ou igual a um, marca como acerto
void *kickar(void *tid)
{
    int k, i; // variaveis auxiliares
    double X, Y, R; 
	unsigned long hits=0;
 
    k  = (int) tid;

	for(i = 0; i < KICKS; i++)
	{
        pthread_mutex_lock(&MUTEX);
		
		// Distribuicao uniforme 0<x<1;
        X = rand();
        Y = rand();		
		
        pthread_mutex_unlock(&MUTEX);
    
		// define valores aleatorios para X e Y
        X = X/(double)RAND_MAX;	
        Y = Y/(double)RAND_MAX;	
		
		// Pitagoras
        // R = sqrt(X^2) + (Y^2)
		R = (X * X)+(Y * Y);
		// R = sqrt(R)
		R = sqrt(R); 
       
    	//Avaliacao
		if(R <= 1) { hits++; }	
	}

	// define a quantidade de acertos para cada thread
    acc[k] = hits;
 
    pthread_exit(NULL);
}


int main(int argc, char *argv[])
{
	time_t t_zero, t_final;
	double delta_t;
	int i, j = 0, k = 0;

    pthread_t threads[N_THREADS];
    pthread_attr_t attr;

    // Marca o tmepo 
	time(&t_zero);

    // Inicializa pthreads e semaforo
    pthread_mutex_init(&MUTEX, NULL);
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

	// Configura seed das funcoes Random
    srand(get_random());	
	
	// Float com precisao arbitratia
	mpf_set_default_prec(MANTISSA);
	mpf_t pi, aux;
	
	for(i = 0; i < N_THREADS; i++)
	{
		// cria thread
        pthread_create(&threads[i], &attr, kickar, (void*) i);
    }
		  
	for(i = 0; i < N_THREADS; i++)
	{
		// sincroniza
        pthread_join(threads[i], NULL);
    }

	// inicializa as variaveis
    mpf_init(pi);
    mpf_init2(aux, 128); 

    for(i = 0; i < N_THREADS; i++)
	{
		// aux = aux + acc[i]
		mpf_add_ui(aux, aux, acc[i]);
    }
 
	// pi = aux
    mpf_set(pi, aux);
    
	// aux = KICKS
    mpf_set_ui(aux, KICKS);
	// aux = aux * N_THREADS
    mpf_mul_ui(aux, aux, N_THREADS);

	// pi = pi / aux
    mpf_div(pi, pi, aux);
	// pi = pi * 4
    mpf_mul_ui(pi, pi, 4);  
 	
	//mostra o valor de pi
	mpf_out_str(NULL, BASE, 0, pi);
		
	// Libera a memoria
	mpf_clear(aux);
	mpf_clear(pi);

	// destroi semaforos
    pthread_attr_destroy(&attr);
    pthread_mutex_destroy(&MUTEX);
	
	// mostra o tempo gasto
    time(&t_final);	
	delta_t  = difftime(t_final, t_zero);
	printf("\nTempo = %.2lf segundos\n", delta_t);
		
    // espera todas as threads acabarem de rodar antes da main terminar
	pthread_exit(NULL);

	return 0;
}
